package com.ecjtu.stadiumre_servations.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ecjtu.stadiumre_servations.entity.domain.Booking;
import com.ecjtu.stadiumre_servations.entity.domain.DataAnalysis;
import com.ecjtu.stadiumre_servations.entity.domain.Venue;
import com.ecjtu.stadiumre_servations.entity.dto.DataAnalysisDTO.DataDetailedDTO;
import com.ecjtu.stadiumre_servations.entity.dto.DataAnalysisDTO.DataRoughDTO;
import com.ecjtu.stadiumre_servations.entity.dto.DataAnalysisDTO.DataTypeDTO;
import com.ecjtu.stadiumre_servations.mapper.BookingMapper;
import com.ecjtu.stadiumre_servations.mapper.DataAnalysisMapper;
import com.ecjtu.stadiumre_servations.mapper.VenueMapper;
import com.ecjtu.stadiumre_servations.service.DataAnalysisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

import static com.baomidou.mybatisplus.extension.toolkit.Db.save;

/**
 * 使用分析：场地实际使用时间段（排除取消预约），返回map<场馆ID，使用时间段>
 * 服务偏好分析：场地实际使用（排除取消预约），统计数量，返回map<场馆ID，使用次数>
 * 场地使用效率：场地的实际使用时长（排除取消预约），统计时长，返回map<场馆ID，使用时长>
 * 收入分析：个人预约和团队预约占比及利润，个人及团队预约次数及占比
 */

@Service
public class DataAnalysisServiceImpl implements DataAnalysisService {
    @Autowired
    private BookingMapper bookingMapper;
    @Autowired
    private DataAnalysisMapper dataAnalysisMapper;
    @Autowired
    private VenueMapper venueMapper;
    /**
     * 统计全部，一天一次，以天为单位
     */
    public void statsAllForOneDay(LocalDate day)
    {
        //统计时间为00:00:00-23:59:59
        LocalDateTime start = LocalDateTime.of(day.getYear(), day.getMonth(), day.getDayOfMonth(), 0, 0, 0);
        LocalDateTime end = LocalDateTime.of(day.getYear(), day.getMonth(), day.getDayOfMonth(), 23, 59, 59);
        //获取每个场馆的数据
        Map<String, DataDetailedDTO> dataDTOMap = dataAnalysis(start, end);
        //获取所有场馆ID
        List<String> venueID = new ArrayList<>();
        for(Map.Entry<String, DataDetailedDTO> entry : dataDTOMap.entrySet())
        {
            venueID.add(entry.getKey());
        }

        //按自然顺序排序
        venueID.sort(null);
        for (String venueId : venueID)
        {
            DataAnalysis dataAnalysis = new DataAnalysis(venueId, dataDTOMap.get(venueId), day);
            //插入数据
            save(dataAnalysis);
        }
    }
    /**
     * 更新当天数据，待支付订单发生变化
     */
    public void updateForOneDay(LocalDate day, String bookingId)
    {
        //获取预约详情
       Booking booking = bookingMapper.selectById(bookingId);
       String venueId = booking.getVenueId();
        //获取场馆ID
       LambdaQueryWrapper<DataAnalysis> wrapper = new LambdaQueryWrapper<>();
       wrapper.eq(DataAnalysis::getStatTime, day)
       .eq(DataAnalysis::getVenueId, venueId);
        //查询是否被统计过
       DataAnalysis dataAnalysis = dataAnalysisMapper.selectOne(wrapper);
       //如果统计过那就更新
       if (dataAnalysis != null)
       {
           System.out.println("updateForOneDay 该场馆今天已统计过，现在更新：" + bookingId);

           dataAnalysis.setPeriod(dataAnalysis.getPeriod() + booking.getBookingStartTime() + "-" + booking.getBookingEndTime() + "-");
           if(booking.getBookingType().equals("个人预约"))
           {
               Duration duration = Duration.between(booking.getBookingStartTime(), booking.getBookingEndTime());
               dataAnalysis.setSingleTime(dataAnalysis.getSingleTime() + duration.toHours());
               dataAnalysis.setSingleProfit(dataAnalysis.getSingleProfit() + booking.getBookingTotalPrice());
               dataAnalysis.setSingleCount(dataAnalysis.getSingleCount() + 1);
           }
           else if (booking.getBookingType().equals("团队预约"))
           {
               Duration duration = Duration.between(booking.getBookingStartTime(), booking.getBookingEndTime());
               dataAnalysis.setGroupTime(dataAnalysis.getGroupTime() + duration.toHours());
               dataAnalysis.setGroupProfit(dataAnalysis.getGroupProfit() + booking.getBookingTotalPrice());
               dataAnalysis.setGroupCount(dataAnalysis.getGroupCount() + 1);
           }
           //时间
           dataAnalysis.setTotalTime(dataAnalysis.getSingleTime() + dataAnalysis.getGroupTime());
           dataAnalysis.setSingleTimeRatio( ( (double)dataAnalysis.getSingleTime() / (double)dataAnalysis.getTotalTime()));
           dataAnalysis.setGroupTimeRatio( ( (double)dataAnalysis.getGroupTime() / (double)dataAnalysis.getTotalTime()));

           //次数
           dataAnalysis.setTotalCount(dataAnalysis.getSingleCount() + dataAnalysis.getGroupCount());
           dataAnalysis.setSingleCountRatio((double) dataAnalysis.getSingleCount() / (double)dataAnalysis.getTotalCount());
           dataAnalysis.setGroupCountRatio((double)dataAnalysis.getGroupCount() / (double)dataAnalysis.getTotalCount());
           //利润
           dataAnalysis.setTotalProfit(dataAnalysis.getSingleProfit() + dataAnalysis.getGroupProfit());
           dataAnalysis.setSingleProfitRatio(dataAnalysis.getSingleProfit() / dataAnalysis.getTotalProfit());
           dataAnalysis.setGroupProfitRatio(dataAnalysis.getGroupProfit() / dataAnalysis.getTotalProfit());

           LambdaUpdateWrapper<DataAnalysis> updateWrapper = Wrappers.lambdaUpdate();
           updateWrapper.eq(DataAnalysis::getVenueId, venueId)
                   .set(DataAnalysis::getPeriod, dataAnalysis.getPeriod())

                   .set(DataAnalysis::getSingleTime, dataAnalysis.getSingleTime())
                   .set(DataAnalysis::getGroupTime, dataAnalysis.getGroupTime())
                   .set(DataAnalysis::getTotalTime, dataAnalysis.getTotalTime())

                   .set(DataAnalysis::getSingleProfit, dataAnalysis.getSingleProfit())
                   .set(DataAnalysis::getGroupProfit, dataAnalysis.getGroupProfit())
                   .set(DataAnalysis::getTotalProfit, dataAnalysis.getTotalProfit())

                   .set(DataAnalysis::getSingleCount, dataAnalysis.getSingleCount())
                   .set(DataAnalysis::getGroupCount, dataAnalysis.getGroupCount())
                   .set(DataAnalysis::getTotalCount, dataAnalysis.getTotalCount())

                   .set(DataAnalysis::getSingleTimeRatio, dataAnalysis.getSingleTimeRatio())
                   .set(DataAnalysis::getGroupTimeRatio, dataAnalysis.getGroupTimeRatio())

                   .set(DataAnalysis::getSingleProfitRatio, dataAnalysis.getSingleProfitRatio())
                   .set(DataAnalysis::getGroupProfitRatio, dataAnalysis.getGroupProfitRatio())

                   .set(DataAnalysis::getSingleCountRatio, dataAnalysis.getSingleCountRatio())
                   .set(DataAnalysis::getGroupCountRatio, dataAnalysis.getGroupCountRatio());

           dataAnalysisMapper.update(null, updateWrapper);
       }
       //没统计过就插入
       else
       {
           System.out.println("updateForOneDay 该场馆今天未统计过，现在插入：" + bookingId);
           DataDetailedDTO p = new DataDetailedDTO();
           //计算数据
           p = statData(booking, p);
           //计算比例
           p= statRatio(p);
           DataAnalysis d = new DataAnalysis(venueId, p, day);
           save(d);
       }
    }
    /**
     * 检定，在统计之前执行
     */
    public void check (LocalDate startDay, LocalDate endDay)
    {
        for (LocalDate day =startDay; day.isBefore(endDay.plusDays(1)); day = day.plusDays(1))
        {
            //查询今天的数据记录
            LambdaQueryWrapper<DataAnalysis> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(DataAnalysis::getStatTime, day);
            //查询这天有没有统计，没有统计则统计并插入数据库
            if (dataAnalysisMapper.selectCount(wrapper) == 0)
            {
                statsAllForOneDay(day);
            }
            //检测这天是否有数据更新
            else
            {
                LocalDateTime start = LocalDateTime.of(day.getYear(), day.getMonth(), day.getDayOfMonth(), 0, 0, 0);
                LocalDateTime end = LocalDateTime.of(day.getYear(), day.getMonth(), day.getDayOfMonth(), 23, 59, 59);

                String payed = "已支付";
                //更新后状态变为‘已支付’
                List<String> bookingPayed = new ArrayList<>();
                List<String> bookingPayedToDay = dataAnalysisMapper.booking_status(payed, start, end);
                for (String bookingId : bookingPayedToDay)
                {
                    String upd = dataAnalysisMapper.selectForUpdate(bookingId);
                    if(upd == null)
                        continue;
                    bookingPayed.add(upd);
                }
                //'已支付'则更新后删除
                if (!bookingPayed.isEmpty())
                {
                    for (String bookingId : bookingPayed)
                    {
                        System.out.println("statsAllByPeriodDetailed 更新" + bookingId);
                        updateForOneDay(day, bookingId);
                        dataAnalysisMapper.deleteForUpdate(bookingId);
                    }
                }
            }
        }

    }
    /**
     * 统计全部，输入天数，start的00:00:00至end的23:59:59，所有记录
     */
    @Override
    public List<DataAnalysis> statsAllByPeriodDetailed(LocalDate startDay, LocalDate endDay, Integer choice, String venueId)
    {
        check(startDay, endDay);
        //1，查询全部
        if (choice == 1)
        {
            LambdaQueryWrapper<DataAnalysis> wrapper = new LambdaQueryWrapper<>();
            wrapper.between(DataAnalysis::getStatTime, startDay, endDay);
            return dataAnalysisMapper.selectList(wrapper);
        }
        //2，查询指定场馆
        else if (choice == 2)
        {
            LambdaQueryWrapper<DataAnalysis> wrapper = new LambdaQueryWrapper<>();
            wrapper.between(DataAnalysis::getStatTime, startDay, endDay)
                    .eq(DataAnalysis::getVenueId, venueId);
            return dataAnalysisMapper.selectList(wrapper);
        }
        return new ArrayList<>();
    }
    /**
     * 统计全部，输入天数，粗略统计
     */
    @Override
    public DataRoughDTO statsAllByPeriodRough(LocalDate startDay, LocalDate endDay, Integer choice, String venueId)
    {
        Integer totalCount = null;
        Integer totalSingleCount = null;
        Integer totalGroupCount = null;
        Double singleCountRatio = null;
        Double groupCountRatio = null;

        Long totalTime = null;
        Long totalSingleTime = null;
        Long totalGroupTime = null;
        Double singleTimeRatio = null;
        Double groupTimeRatio = null;

        Double totalProfit = null;
        Double totalSingleProfit = null;
        Double totalGroupProfit = null;
        Double singleProfitRatio = null;
        Double groupProfitRatio = null;
        if (choice ==1)
        {
            totalCount = dataAnalysisMapper.totalCount(startDay, endDay);
            totalSingleCount = dataAnalysisMapper.totalSingleCount(startDay, endDay);
            totalGroupCount = dataAnalysisMapper.totalGroupCount(startDay, endDay);
            singleCountRatio = (double)totalSingleCount / (double)totalCount;
            groupCountRatio = (double)totalGroupCount / (double)totalCount;

            totalTime = dataAnalysisMapper.totalTime(startDay, endDay);
            totalSingleTime = dataAnalysisMapper.totalSingleTime(startDay, endDay);
            totalGroupTime = dataAnalysisMapper.totalGroupTime(startDay, endDay);
            singleTimeRatio = (double)totalSingleTime / (double)totalTime;
            groupTimeRatio = (double)totalGroupTime / (double)totalTime;

            totalProfit = dataAnalysisMapper.totalProfit(startDay, endDay);
            totalSingleProfit = dataAnalysisMapper.totalSingleProfit(startDay, endDay);
            totalGroupProfit = dataAnalysisMapper.totalGroupProfit(startDay, endDay);
            singleProfitRatio = totalSingleProfit / totalProfit;
            groupProfitRatio = totalGroupProfit / totalProfit;
        }
        else if (choice ==2)
        {
            totalCount = dataAnalysisMapper.totalCountById(startDay, endDay, venueId);
            totalSingleCount = dataAnalysisMapper.totalSingleCountById(startDay, endDay, venueId);
            totalGroupCount = dataAnalysisMapper.totalGroupCountById(startDay, endDay, venueId);
            singleCountRatio = (double)totalSingleCount / (double)totalCount;
            groupCountRatio = (double)totalGroupCount / (double)totalCount;

            totalTime = dataAnalysisMapper.totalTimeById(startDay, endDay, venueId);
            totalSingleTime = dataAnalysisMapper.totalSingleTimeById(startDay, endDay, venueId);
            totalGroupTime = dataAnalysisMapper.totalGroupTimeById(startDay, endDay, venueId);
            singleTimeRatio = (double)totalSingleTime / (double)totalTime;
            groupTimeRatio = (double)totalGroupTime / (double)totalTime;

            totalProfit = dataAnalysisMapper.totalProfitById(startDay, endDay, venueId);
            totalSingleProfit = dataAnalysisMapper.totalSingleProfitById(startDay, endDay, venueId);
            totalGroupProfit = dataAnalysisMapper.totalGroupProfitById(startDay, endDay, venueId);
            singleProfitRatio = totalSingleProfit / totalProfit;
            groupProfitRatio = totalGroupProfit / totalProfit;
        }

        return new DataRoughDTO(totalCount, totalSingleCount, totalGroupCount, singleCountRatio, groupCountRatio,
                totalTime, totalSingleTime, totalGroupTime, singleTimeRatio, groupTimeRatio,
                totalProfit, totalSingleProfit, totalGroupProfit, singleProfitRatio, groupProfitRatio);
    }
    /**
     * 查询条件
     */
    public List<Booking> select(LocalDateTime start, LocalDateTime end)
    {
        LambdaQueryWrapper<Booking> queryWrapper = Wrappers.lambdaQuery();
        //重叠，统计开始时间或者结束时间在预约时间内即可
        queryWrapper.and(qw -> qw.between(Booking::getBookingStartTime, start, end)
                .or().between(Booking::getBookingEndTime, start, end));

        return bookingMapper.selectList(queryWrapper);
    }
    /**
     * 统计 时间段，时间，利润，次数
     */
    public Map<String, DataDetailedDTO> dataAnalysis(LocalDateTime start, LocalDateTime end)
    {
        //查询某天时间段内所有预约
        List<Booking> bookings= select(start, end);
        //<场馆ID，数据>
        Map<String, DataDetailedDTO> map = new HashMap<>();
        //计算三种数据及时间段
        for (Booking booking : bookings)
        {
            //已取消和待支付的预约不计入
            if(booking.getBookingStatus().equals("已取消") || booking.getBookingStatus().equals("待支付"))
                continue;
            //如果map中没有场馆ID对应值，新建一个空值
            if(map.get(booking.getVenueId()) == null)
            {
                DataDetailedDTO dataDetailedDTO = new DataDetailedDTO();
                map.put(booking.getVenueId(), dataDetailedDTO);
            }
            DataDetailedDTO p = map.get(booking.getVenueId());
            p = statData(booking, p);
            map.put(booking.getVenueId(), p);
        }
        //计算比例
        for(Map.Entry<String, DataDetailedDTO> entry : map.entrySet())
        {
            DataDetailedDTO p = entry.getValue();
            p = statRatio(p);
        }
        return map;
    }
    /**
     * 按场馆类型统计数据
     */
    @Override
    public Map<String, DataTypeDTO> statByTypeRough(LocalDate start, LocalDate end)
    {
        List <DataAnalysis> dataAnalysisList = statsAllByPeriodDetailed(start, end, 1, "");
        Map<String, DataTypeDTO> map = new HashMap<>();

        Integer totalCount = 0;
        Long totalTime = 0L;
        Double totalProfit = 0D;

        for (DataAnalysis dataAnalysis : dataAnalysisList)
        {
            String type = dataAnalysis.getVenueType();
            if (map.get(type) == null)
            {
                DataTypeDTO dataTypeDTO = new DataTypeDTO();
                map.put(type, dataTypeDTO);
            }
            DataTypeDTO nowTypeDTO = map.get(type);

            nowTypeDTO.setTotalCount(nowTypeDTO.getTotalCount() + dataAnalysis.getTotalCount());
            totalCount += dataAnalysis.getTotalCount();

            nowTypeDTO.setTotalTime(nowTypeDTO.getTotalTime() + dataAnalysis.getTotalTime());
            totalTime += dataAnalysis.getTotalTime();

            nowTypeDTO.setTotalProfit(nowTypeDTO.getTotalProfit() + dataAnalysis.getTotalProfit());
            totalProfit += dataAnalysis.getTotalProfit();
        }

        for (Map.Entry<String, DataTypeDTO> entry : map.entrySet())
        {
            DataTypeDTO dataTypeDTO = entry.getValue();
            dataTypeDTO.setCountRatio((double)dataTypeDTO.getTotalCount() / (double)totalCount);
            dataTypeDTO.setTimeRatio((double)dataTypeDTO.getTotalTime() / (double)totalTime);
            dataTypeDTO.setProfitRatio(dataTypeDTO.getTotalProfit() / totalProfit);
            map.put(entry.getKey(), dataTypeDTO);
        }
        return map;
    }
    /**
     * 3种数据
     */
    public DataDetailedDTO statData (Booking booking, DataDetailedDTO d)
    {
        //时间段，直接使用
        String period = "";
        period = booking.getBookingStartTime() + "-" + booking.getBookingEndTime() + "-";
        d.setPeriod(d.getPeriod() + period);
        //场馆类型，查询即可
        LambdaQueryWrapper<Venue> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.select(Venue::getVenueType)
                .eq(Venue::getVenueId, booking.getVenueId());
        d.setVenueType(venueMapper.selectOne(lambdaQueryWrapper).getVenueType());
        //个人数据统计
        if(booking.getBookingType().equals("个人预约"))
        {
            //个人时间
            Duration duration = Duration.between(booking.getBookingStartTime(), booking.getBookingEndTime());
            d.setSingleTime(d.getSingleTime() + duration.toHours());
            //个人利润
            d.setSingleProfit(d.getSingleProfit() + booking.getBookingTotalPrice());
            //个人次数，每次都加1
            d.setSingleCount(d.getSingleCount() + 1);
        }
        //团队数据统计
        else if (booking.getBookingType().equals("拼团预约"))
        {
            //团队时间
            Duration duration = Duration.between(booking.getBookingStartTime(), booking.getBookingEndTime());
            d.setGroupTime(d.getGroupTime() + duration.toHours());
            //团队利润
            d.setGroupProfit(d.getGroupProfit() + booking.getBookingTotalPrice());
            //团队次数
            d.setGroupCount(d.getGroupCount() + 1);
        }
        return d;
    }
    /**
     * 3种数据比例
     */
    public DataDetailedDTO statRatio (DataDetailedDTO d)
    {
        //时间
        d.setTotalTime(d.getSingleTime() + d.getGroupTime());
        d.setSingleTimeRatio( ( (double) d.getSingleTime() / (double) d.getTotalTime()));
        d.setGroupTimeRatio( ( (double) d.getGroupTime() / (double) d.getTotalTime()));
        //次数
        d.setTotalCount(d.getSingleCount() + d.getGroupCount());
        d.setSingleCountRatio((double) d.getSingleCount() / (double) d.getTotalCount());
        d.setGroupCountRatio((double) d.getGroupCount() / (double) d.getTotalCount());
        //利润
        d.setTotalProfit(d.getSingleProfit() + d.getGroupProfit());
        d.setSingleProfitRatio(d.getSingleProfit() / d.getTotalProfit());
        d.setGroupProfitRatio(d.getGroupProfit() / d.getTotalProfit());

        return d;
    }

}
